package ChessEngine;

import java.io.BufferedWriter;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Queue;

import javax.swing.text.AbstractDocument.BranchElement;

import ChessEngine.moveGenerator.MoveGenerator;
import boardData.*;

/**
 * Write a description of class GameTree here.
 * 
 * @author (Nico Becker)
 * @version (0.5 30.06.2011)
 */
public class GameTree {
	private Node root;

	private Node actualPosition;
	private AbstractEvalutor evaluator;

	public GameTree() {

	}

	/**
	 * Konstruktor erstellt den Spielbaum
	 * 
	 * @param br
	 *            Boardpr�sentation, als Objekt, muss entsprechend gecastet
	 *            werden
	 * @param searchdepth
	 *            Tiefe des Baumes der erstellt werden soll
	 */
	public GameTree(Board br, boolean isWhiteMove) {
		evaluator = new SimpleEvaluator();
		// Erster Knoten (Wurzel) hat genau ein Element in der Liste, eben die
		// Startposition
		LinkedList<Board> playerChoices = new LinkedList<Board>();
		playerChoices.add(br);
		// Dieser Knoten ist die Wurzel des Spielbaumes
		root = new Node(br, null, isWhiteMove);
		// Die Wurzel des Spielbaumes hat keinen Vater
		// root.setFather(null);
		// Die aktuelle Position im Baum ist die Wurzel
		actualPosition = root;

	}

	/**
	 * durchl�uft den Spielbaum nach bfs strategie
	 */
	public void breathFirstSearch(Node root) {

		final LinkedList<Node> childrenList = root.getList();

		/*
		 * rekursionsanker, falls keine kinder vorhanden, aufhoeren
		 */
		if (childrenList.isEmpty()) {
			return;
		}

		/*
		 * die Kinderliste erst durchlaufen
		 */
		for (int i = 0; i < childrenList.size(); i++) {
			final Node child = childrenList.get(i);
			// hier koennte nach dem Visitor musster eine Objekt an den Knoten
			// uebergeben werden dass
			// eine definierte methode besitzt die dann vom knotenobjekt
			// ausgefuehrt wird.
			// child.accept(v);
		}

		/*
		 * dann beim ersten Kind in die rekursion
		 */
		for (int i = 0; i < childrenList.size(); i++) {
			final Node child = childrenList.get(i);
			breathFirstSearch(child);
		}
	}

	public Node getRoot() {
		return this.root;

	}

	public void setRootNode(Board b, boolean isWhiteMove) {
		Node newRootNode = new Node(b, null, isWhiteMove);
		this.root = newRootNode;
	}

	/**
	 * Fuegt dem Spielbaum an <code>actualPosition</code> die uebergebenen
	 * moeglichen Spielzuege an actualPosition wird auf das erste element der
	 * neu erstellten Liste gesetzt
	 * 
	 */
	// public void insert(LinkedList<Board> possibleMoves, Node father){
	//
	// if(!possibleMoves.isEmpty()){
	// LinkedList<Node> childNodes = new LinkedList<Node>();
	//
	// for(int i=0; i<possibleMoves.size();i++){
	// final Board b = possibleMoves.get(i);
	// final Node n = new Node(b, actualPosition);
	// childNodes.add(n);
	// }
	// evaluateAndSort(childNodes);
	// father.setList(childNodes);
	//
	// }
	// }

	private void evaluateAndSort(LinkedList<Node> playerChoices) {

		// ueber die Liste laufen, jeden Knoten bewerten, asnchlie�end Liste
		// sortieren

		ListIterator<Node> iterator = playerChoices.listIterator();
		while (iterator.hasNext()) {
			Node tmpNode = iterator.next();
			final int eval = evaluator.evaluate(tmpNode.getBoardData());
			tmpNode.setEvaluation(eval);
		}
		Collections.sort(playerChoices, new WhiteNodeComperator());
	}

	// public Node contains(Board b){
	//
	// return new Node(b,this);
	// }

	public void print() {
		this.root.print("");
	}

	public void print(BufferedWriter out) throws IOException {
		this.root.print("",out);
		
	}

}

class Node {
	private LinkedList<Node> childNodes; // Liste die auf die Kinder verweist ->
											// ALLE generierten
											// Zugmoeglichkeiten
	private Board boardData; // aktuelle Boardrepraesentation
	private boolean whiteMove;
	private int nodeEvaluation =0; // Bewertung des aktuellen Boards
//	private Node father; // Vater des aktuellen Knotens
//	private String bestMove;
	public boolean cutOff=false;

	/**
	 * Konstruktor erzeugt ein neuer Knoten ruft den ersten Konstruktor auf
	 * 
	 * @param br
	 *            aktuelle Boardpr�sentation
	 * @param father
	 *            Vater des aktuellen Knotens
	 */
	public Node(Board br, Node father, boolean isWhiteMove) {
		boardData = br;
//		this.father = father;
		this.whiteMove = isWhiteMove;
	}

	

	public boolean isWhiteMove() {
		return whiteMove;
	}

	public void setWhiteMove(boolean whiteMove) {
		this.whiteMove = whiteMove;
	}

	/**
	 * Liefert die zu der Boardpr�sentation zugeh�rigen Folgez�ge in einer Liste
	 * zur�ck
	 * 
	 * @return Folgez�ge in einer LinkedList
	 */
	public LinkedList<Node> getList() {
		return this.childNodes;
	}

	/**
	 * Setzt die zu der Boardprepraesentation zugehoerigen Folgezuege
	 * 
	 * @param LinkedList
	 *            Folgezuege welche vom Zuggenerator generiert worden sind
	 */
	public void setList(LinkedList<Board> boardPlayerChoiceList) {
		LinkedList<Node> tmpList = new LinkedList<Node>();
		for (int i = 0; i < boardPlayerChoiceList.size(); i++) {
			Node tmpNode = new Node(boardPlayerChoiceList.get(i), this,
					!this.isWhiteMove());
			tmpList.add(tmpNode);
		}
		this.childNodes = tmpList;
	}

	/**
	 * Setzt den Vater des aktuellen Knotens
	 * 
	 * @param father
	 *            Vater der gesetzt werden soll
	 */
//	public void setFather(Node father) {
//		this.father = father;
//	}
//	
	
	/**
	 * Gibt den Vaten zum aktuellen Knoten zurueck
	 * @return
	 */
//	public Node getFather() {
//		return this.father;
//	}

	/**
	 * Liefert die aktuelle Boardpr�sentation zur�ck Sollte nach R�ckgabe
	 * gecastet werden
	 */
	public Board getBoardData() {
		return this.boardData;
	}

	public void setEvaluation(int evaluation) {
		this.nodeEvaluation = evaluation;
	}

	public int getEvaluation() {
		return nodeEvaluation;

	}

	public boolean hasChildren() {

		if (this.childNodes == null || this.childNodes.size() == 0) {
			return false;
		}
		return true;
	}

//	public boolean isRootNode() {
//		return this.father == null;
//	}

	public void print(String indent) {
		if (this.isWhiteMove()) {
//			if(this.getList()!= null){
//				System.out.println(indent+"Anzahl Kinder: "+this.getList().size());	
//			}
			
			System.out.println(indent + "W:" + this.getEvaluation()+((cutOff)?"cut":""));
//					+ " Bester Zug: " + this.bestMove);
		} else {
			System.out.println(indent + "B" + this.getEvaluation()+((cutOff)?"cut":""));
//					+ " Bester Zug: " + this.bestMove);
		}
		
		//this.getBoardData().print(indent);
//		this.getBoardData().toString();
		indent = indent + "\t";
		if (this.hasChildren()) {
			for (Node child : this.childNodes) {
				child.print(indent);
			}
		}

		return;

	}
	
	public void print(String indent, BufferedWriter out) throws IOException {
//		final Integer ebene = new Integer((indent.split("\t")).length);
//		out.write(indent+"ebene "+ebene+"\n");

		if (this.isWhiteMove()) {
//			if(this.getList()!= null){
//				out.write(indent+"whiteMove\n Anzahl Kinder: "+this.getList().size()+"\n");	
//				
//			}
			out.write(indent+"W"+this.getEvaluation()+((cutOff)?"cut":"")+"\n");
//			out.write(indent + "W:" + this.getEvaluation()
//					+ " Bester Zug: " + this.bestMove+"\n");
		} else {
//			if(this.getList()!= null){
//				out.write(indent+ "blackMove\n Anzahl Kinder: "+this.getList().size()+"\n");
//				
//			}
			out.write(indent+"B"+this.getEvaluation()+((cutOff)?"cut":"")+"\n");
//			out.write(indent + "B" + this.getEvaluation()
//					+ " Bester Zug: " + this.bestMove+"\n");
		}
//		this.getBoardData().print(indent,out);
		indent = indent + "\t";
		if (this.hasChildren()) {
			for (Node child : this.childNodes) {

				if(child.getEvaluation()==0){
					out.write(indent+"cut\n");
//					continue;
				}
				child.print(indent,out);
			}
		}

		return;
		
	}

//	public void setBestMove(String move) {
//		this.bestMove = move;
//	}

//	public Object getBestMove() {
//		return bestMove;
//	}



	public void setNodeList(LinkedList<Node> nodeList) {
		this.childNodes =nodeList;
		
	}
}

class WhiteNodeComperator implements Comparator<Node> {

	@Override
	public int compare(Node o1, Node o2) {
		final int nodeEvalOne = o1.getEvaluation();
		final int nodeEvalTwo = o2.getEvaluation();

		if (nodeEvalOne > nodeEvalTwo) {
			return 1;
		} else if (nodeEvalOne == nodeEvalTwo) {
			return 0;
		} else {
			return -1;
		}
	}
}

	class BlackNodeComperator implements Comparator<Node> {

		@Override
		public int compare(Node o1, Node o2) {
			final int nodeEvalOne = o1.getEvaluation();
			final int nodeEvalTwo = o2.getEvaluation();

			if (nodeEvalOne > nodeEvalTwo) {
				return -1;
			} else if (nodeEvalOne == nodeEvalTwo) {
				return 0;
			} else {
				return 1;
			}
		}
	}


